const fs = require("fs");
const path = require("path");
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const { transformFromAst } = require("@babel/core");

const { dirExists } = require('./file.js');

module.exports = class webpack {
  constructor(options) {
    this.entry = options.entry;
    this.output = options.output;
    this.modules = [];
  }
  run() {
    const info = this.parse(this.entry);
    this.modules.push(info);

    for (let i = 0; i < this.modules.length; i++) {
      const item = this.modules[i];
      const { yilai } = item;
      if (yilai) {
        for (let j in yilai) {
          this.modules.push(this.parse(yilai[j]));
        }
      }
    }
    //格式转换
    const obj = {};
    this.modules.forEach((item) => {
      obj[item.entryFile] = {
        yilai: item.yilai,
        code: item.code,
      };
    });
    this.file(obj);
  }

  
  // 暗号：有点感动了 怎么办？
  parse (entryFile) {
    const content = fs.readFileSync(entryFile, 'utf-8');
    
    // 将内容转换成抽象语法树
    const AST = parser.parse(content, {
      sourceType: 'module'
    });

    const dependency = {};

    traverse(AST, {
      ImportDeclaration ({ node }) {
        // 保存依赖的两种路径 相对于引入文件的路径 和相对于项目根目录的路径
        const newPathName = `./${path.join(path.dirname(entryFile), node.source.value)}`;

        dependency[node.source.value] = newPathName;
      }
    });

    const { code } = transformFromAst(AST, null, {
      presets: ['@babel/preset-env']
    });

    return {
      entryFile,
      yilai: dependency,
      code
    }
  }


  async file(code) {
    await dirExists(this.output.path);

    const filePath = path.join(this.output.path, this.output.filename);

    const newCode = JSON.stringify(code);
    const bundle = `(function(graph){
        function require(module){
            function pathRequire(realtivePath){
               return require(graph[module].yilai[realtivePath])
            }
            var exports = {};
            (function(require,exports,code){
                eval(code)
            })(pathRequire,exports,graph[module].code)
            return exports;
        }
        require('${this.entry}')
    })(${newCode})`;


    //./src/index.js
    // require("./b.js");
    fs.writeFileSync(filePath, bundle, {
      encoding: "utf-8",
      flag: 'w'
    });
  }
};
